package cn.lg.soar.database.datascope;

import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.core.annotations.DataScope;
import cn.lg.soar.core.util.SoarSubscriber;
import cn.lg.soar.database.enums.ExpressionEnum;
import cn.lg.soar.database.enums.SpliceTypeEnum;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.schema.Column;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * @author luguoxiang 469599794@qq.com
 * @Date: 2023/10/27 23:28
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class DataScopeUtils {

    /**
     * 无数据权限
     */
    public static final List<List<Filter>> NOT_DATA_SCOPE = Collections.emptyList();
    /**
     * 全部数据权限
     */
    public static final List<List<Filter>> ALL_DATA_SCOPE = Collections.singletonList(Collections.emptyList());

    private static final HexValue NOT_DATA_PERMISSION = new HexValue("1=2");

    private static IDataPermissionService dataPermissionService;
    static {
        SoarSubscriber.subscribeInit(applicationContext -> {
            dataPermissionService = applicationContext.getBean(IDataPermissionService.class);
        });
    }

    /**
     * 获取数据权限sql
     * @param resourceCode 资源code
     * @param table 表名（别名）
     * @return
     */
    public static String getSqlAs(String resourceCode, String table) {
        List<List<Filter>> lists = dataPermissionService.dataScopes(resourceCode);
        if (DataUtil.isValuable(table)) {
            lists.forEach(x -> x.forEach(f -> {
                if (!f.getColumnName().contains(".")) {
                    f.setColumnName(table +"."+ f.getColumnName());
                }
            }));
        }
        return getSql(lists);
    }

    /**
     * 获取数据权限sql
     * @param table 表名（别名）
     * @return
     */
    public static String getSqlAs(String table) {
        if (DataScopeHolder.isIgnore()) {
            return "";
        }
        DataScope dataScope = DataScopeHolder.getResourceCode();
        if (dataScope == null) {
            return "";
        }
        return getSqlAs(dataScope.code(), table);
    }

    /**
     * 获取数据权限sql
     * @return
     */
    public static String getSql() {
        if (DataScopeHolder.isIgnore()) {
            return "";
        }
        DataScope dataScope = DataScopeHolder.getResourceCode();
        if (dataScope == null) {
            return "";
        }
        return getSqlAs(dataScope.code(), dataScope.table());
    }

    /**
     * 获取数据权限sql
     * @param resourceCode 资源编码
     * @return
     */
    public static String getSql(String resourceCode) {
        return getSql(dataPermissionService.dataScopes(resourceCode));
    }

    /**
     * 获取数据权限sql
     * @param dataScopeGroup 数据权限分组
     * @return
     */
    public static String getSql(List<List<Filter>> dataScopeGroup) {
        Expression expression = getExpression(dataScopeGroup);
        if (expression == null) {
            return "";
        }
        // 拼接筛选条件
        if (expression instanceof ParenthesedExpressionList) {
            return expression.toString();
        }
        return new ParenthesedExpressionList<>(expression).toString();
    }

    /**
     * 获取数据权限表达式
     * @param resourceCode 资源code
     * @return
     */
    public static Expression getExpression(String resourceCode, String table) {
        List<List<Filter>> lists = dataPermissionService.dataScopes(resourceCode);
        if (DataUtil.isValuable(table)) {
            lists.forEach(x -> x.forEach(f -> {
                if (!f.getColumnName().contains(".")) {
                    f.setColumnName(table +"."+ f.getColumnName());
                }
            }));
        }
        return getExpression(lists);
    }

    public static Expression getExpression(String resourceCode) {
        return getExpression(dataPermissionService.dataScopes(resourceCode));
    }

    /**
     * 获取数据权限表达式
     * @param dataScopeGroup 数据权限分组
     * @return
     */
    public static Expression getExpression(List<List<Filter>> dataScopeGroup) {
        // 空表示无数据权限
        if (DataUtil.isEmpty(dataScopeGroup)) {
            return NOT_DATA_PERMISSION;
        }
        Expression groupExpression = null;
        // 数据权限分组
        for (List<Filter> dataScopes : dataScopeGroup) {
            // 空代表全部数据权限，因为没有配置数据筛选规则，而只要有全部数据权限的规则，其他规则直接忽略
            if (DataUtil.isEmpty(dataScopes)) {
                return null;
            }
            Expression ruleExpression = null;
            // 数据规则解析
            for (Filter dataScope : dataScopes) {
                Object rawValue = dataScope.getColumnValue();
                if (rawValue == null) {
                    continue;
                }
                // 生成条件表达式
                Expression condition = getCondition(dataScope, rawValue);
                // and or 处理
                if (ruleExpression == null) {
                    ruleExpression = condition;
                } else if (SpliceTypeEnum.or.equals(dataScope.getSpliceType())) {
                    ruleExpression = new OrExpression(ruleExpression, condition);
                } else {
                    ruleExpression = new AndExpression(ruleExpression, condition);
                }
            }
            // 多个角色处理（多个角色分配了数据权限，使用或进行处理）
            ParenthesedExpressionList<Expression> parenthesis = new ParenthesedExpressionList<>(ruleExpression);
            if (groupExpression == null) {
                groupExpression = parenthesis;
            } else {
                groupExpression = new OrExpression(groupExpression, parenthesis);
            }
        }

        if (groupExpression == null) {
            return NOT_DATA_PERMISSION;
        }

        return groupExpression;
    }

    /**
     * 生成查询条件表达式
     * @param dataScope
     * @param rawValue
     * @return
     */
    private static Expression getCondition(Filter dataScope, Object rawValue) {
        Column column = new Column(dataScope.getColumnName());
        ExpressionEnum expression = dataScope.getExpression();
        switch (expression) {
            case in:
                return new InExpression(column, getListValue(rawValue));
            case notIn:
                InExpression notIn = new InExpression(column, getListValue(rawValue));
                notIn.setNot(true);
                return notIn;
            case like:
                LikeExpression like = new LikeExpression();
                like.setLeftExpression(column);
                like.setRightExpression(getValue("%"+ rawValue +"%"));
                return like;
            case likeRight:
                LikeExpression likeRight = new LikeExpression();
                likeRight.setLeftExpression(column);
                likeRight.setRightExpression(getValue(rawValue +"%"));
                return likeRight;
            case likeLeft:
                LikeExpression likeLeft = new LikeExpression();
                likeLeft.setLeftExpression(column);
                likeLeft.setRightExpression(getValue("%"+ rawValue));
                return likeLeft;
            case isNull:
                IsNullExpression isNull = new IsNullExpression();
                isNull.setLeftExpression(column);
                return isNull;
            case isNotNull:
                IsNullExpression isNotNull = new IsNullExpression();
                isNotNull.setLeftExpression(column);
                isNotNull.setNot(true);
                return isNotNull;
            case lt:
                MinorThan lt = new MinorThan();
                lt.setLeftExpression(column);
                lt.setRightExpression(getValue(rawValue));
                return lt;
            case le:
                MinorThanEquals le = new MinorThanEquals();
                le.setLeftExpression(column);
                le.setRightExpression(getValue(rawValue));
                return le;
            case gt:
                GreaterThan gt = new GreaterThan();
                gt.setLeftExpression(column);
                gt.setRightExpression(getValue(rawValue));
                return gt;
            case ge:
                GreaterThanEquals ge = new GreaterThanEquals();
                ge.setLeftExpression(column);
                ge.setRightExpression(getValue(rawValue));
                return ge;
            case ne:
                return new NotEqualsTo(column, getValue(rawValue));
            default:
                return new EqualsTo(column, getValue(rawValue));
        }
    }

    /**
     * 获取单个值
     * @param value
     * @return
     */
    private static Expression getValue(Object value) {
        if (value instanceof Long || value instanceof Integer) {
            return new LongValue(value.toString());
        }
        if (value instanceof BigDecimal || value instanceof Double || value instanceof Float) {
            return new DoubleValue(value.toString());
        }
        if (value instanceof Boolean) {
            return new LongValue(Boolean.TRUE.equals(value) ? 1 : 0);
        }
        return new StringValue(value.toString());
    }

    /**
     * 获取多个值
     * @param value
     * @return
     */
    private static ExpressionList getListValue(Object value) {
        // 集合
        if (value instanceof Collection) {
            Collection<?> values = (Collection<?>) value;
            List<Expression> expressions = new ArrayList<>(values.size());
            for (Object o : values) {
                expressions.add(getValue(o));
            }
            return new ExpressionList(expressions);
        }

        // 数组
        if (value.getClass().isArray()) {
            Object[] array = (Object[]) value;
            List<Expression> expressions = new ArrayList<>(array.length);
            for (Object o : array) {
                expressions.add(getValue(o));
            }
            return new ExpressionList(expressions);
        }

        // 迭代器
        if (value instanceof Iterable) {
            List<Expression> expressions = new ArrayList<>();
            ((Iterable<?>) value).forEach(o -> {
                expressions.add(getValue(o));
            });
            return new ExpressionList(expressions);
        }

        // 单个值
        List<Expression> expressions = new ArrayList<>(1);
        expressions.add(getValue(value));
        return new ExpressionList(expressions);
    }

}